package com.v1.weibo4j;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.map.MultiValueMap;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import weibo4j.Account;
import weibo4j.Friendships;
import weibo4j.Oauth;
import weibo4j.Timeline;
import weibo4j.Trend;
import weibo4j.Users;
import weibo4j.http.AccessToken;
import weibo4j.model.Paging;
import weibo4j.model.Status;
import weibo4j.model.StatusWapper;
import weibo4j.model.User;
import weibo4j.model.UserTrend;
import weibo4j.model.UserWapper;
import weibo4j.model.WeiboException;
import weibo4j.org.json.JSONObject;

import com.v1.model.Diary;
import com.v1.model.Pair;

//trendid is auto-increment
public class Weibo4jLO implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static final Log log = LogFactory.getLog(Weibo4jLO.class);
	private static final int FEATURE = 1;
	private static final int BASE_APP = 0;
	private String access_token;
	private User user;
	private boolean isLogin;
	private Friendships friendships;
	private Trend trend;
	private Timeline timeline;
	private Shorturl shorturl;

	private List<User> friends;
	private List<UserTrend> trends;
	private Map<User, List<UserTrend>> friendAndHisTrends;
	private MultiValueMap trendPaticipators;

	public void authorize(String code) throws Exception {
		Oauth oauth = new Oauth();
		AccessToken accessToken = oauth.getAccessTokenByCode(code);
		long expireIn = NumberUtils.toLong(accessToken.getExpireIn());
		log.debug("expireIn is " + expireIn);
		log.debug("RefreshToken is " + accessToken.getRefreshToken());
		log.debug("AccessToken is " + accessToken.getAccessToken());
		if (expireIn < 1) {
			// access_token = accessToken.getRefreshToken();
			log.info("Token expired");
			throw new WeiboException("Token expired", 21315);
		} else {
			access_token = accessToken.getAccessToken();
		}

		Account am = new Account();
		am.setToken(access_token);
		JSONObject uid = am.getUid();

		Users um = new Users();
		um.setToken(access_token);
		user = um.showUserById(uid.getString("uid"));

		isLogin = true;

		friendships = new Friendships();
		friendships.setToken(access_token);

		trend = new Trend();
		trend.setToken(access_token);

		timeline = new Timeline();
		timeline.setToken(access_token);

		shorturl = new Shorturl();
		shorturl.setToken(access_token);

		init();
	}

	public void init() throws Exception {
		friends = getFriends(user.getId());
		trends = getTrends(user.getId());
		mapFriendAndTrend();
	}

	public void refresh() throws Exception {
		friends = null;
		trends = null;
		friendAndHisTrends = null;
		trendPaticipators = null;

		init();
	}

	public void destroy() {
		access_token = null;
		user = null;
		isLogin = false;
		friendships = null;
		trend = null;
		timeline = null;
		shorturl = null;

		friends = null;
		trends = null;
		friendAndHisTrends = null;
		trendPaticipators = null;
	}

	// ---<friends>-------------------
	public List<User> getFriends() {
		return friends;
	}

	public List<User> getFriends(String uid) throws WeiboException {
		Paging page = new Paging(1, 50);
		List<User> list = new ArrayList<User>();
		getFriends(uid, page, list);
		return list;
	}

	private void getFriends(String uid, Paging page, List<User> list) throws WeiboException {
		UserWapper userWapper = friendships.getFriendsBilateral(uid, 0, page);
		List<User> users = userWapper.getUsers();
		if (CollectionUtils.isEmpty(users)) {
			return;
		}
		list.addAll(users);
		long totalNumber = userWapper.getTotalNumber();
		if (totalNumber <= page.getPage() * page.getCount()) {
			return;
		}
		page.setPage(page.getPage() + 1);
		getFriends(uid, page, list);
	}

	// ---<trends>-------------------
	public List<UserTrend> getTrends() throws WeiboException {
		return trends;
	}

	public List<UserTrend> getTrends(String uid) throws WeiboException {
		Paging page = new Paging(1, 10);
		List<UserTrend> list = new ArrayList<UserTrend>();
		getTrends(uid, page, list);
		return list;
	}

	private void getTrends(String uid, Paging page, List<UserTrend> list) throws WeiboException {
		List<UserTrend> userTrends = trend.getTrends(uid, page);
		if (CollectionUtils.isEmpty(userTrends)) {
			return;
		}
		list.addAll(userTrends);
		int size = userTrends.size();
		if (size < page.getPage() * page.getCount()) {
			return;
		}
		page.setPage(page.getPage() + 1);
		getTrends(uid, page, list);
	}

	// ---<timeline>-------------------
	public List<Status> getTimeline() throws WeiboException {
		return getTimeline(user.getId());
	}

	public List<Status> getTimeline(String uid) throws WeiboException {
		Paging page = new Paging(1, 50);
		List<Status> list = new ArrayList<Status>();
		getTimeline(uid, page, list);
		return list;
	}

	private void getTimeline(String uid, Paging page, List<Status> list) throws WeiboException {
		StatusWapper statusWapper = timeline.getUserTimelineByUid(uid, page, BASE_APP, FEATURE);
		List<Status> statuses = statusWapper.getStatuses();
		if (CollectionUtils.isEmpty(statuses)) {
			return;
		}
		list.addAll(statuses);
		long totalNumber = statusWapper.getTotalNumber();
		if (totalNumber <= page.getPage() * page.getCount()) {
			return;
		}
		page.setPage(page.getPage() + 1);
		getTimeline(uid, page, list);
	}

	// ---<trendline>-------------------
	public List<Diary> getTrendline() throws WeiboException {
		if (CollectionUtils.isEmpty(trends)) {
			return null;
		}
		return getTrendline(trends.get(0).getHotword());
	}

	public List<Diary> getTrendline(String hotword) throws WeiboException {
		String mark = "#" + hotword + "#";
		List<Diary> list = new ArrayList<Diary>();
		// getTrendline(hotword, user, list, mark);
		if (!trendPaticipators.containsKey(hotword)) {
			return null;
		}
		@SuppressWarnings("unchecked")
		List<User> paticipators = (List<User>) trendPaticipators.get(hotword);
		for (User paticipator : paticipators) {
			getTrendline(hotword, paticipator, list, mark);
		}

		Collections.sort(list);
		return list;
	}

	private void getTrendline(String hotword, User user, List<Diary> list, String mark) throws WeiboException {
		String uid = user.getId();
		List<Status> status = getTimeline(uid);
		for (Status s : status) {
			String text = s.getText();
			if (text.contains(mark)) {
				list.add(new Diary(s));
			}
		}
	}

	// ---<all friends' trends>-------------------
	public List<Pair<User, List<UserTrend>>> getAllFriendTrends() throws WeiboException {
		// List<UserTrend> trends = getTrends();
		// map.put(user, trends);
		LinkedList<Pair<User, List<UserTrend>>> list = new LinkedList<Pair<User, List<UserTrend>>>();
		for (User friend : friendAndHisTrends.keySet()) {
			List<UserTrend> trends = friendAndHisTrends.get(friend);
			Pair<User, List<UserTrend>> pair = new Pair<User, List<UserTrend>>(friend, trends);
			if (CollectionUtils.isNotEmpty(trends)) {
				list.addFirst(pair);
			} else {
				list.addLast(pair);
			}
		}
		return list;
	}

	private void mapFriendAndTrend() throws WeiboException {
		friendAndHisTrends = new HashMap<User, List<UserTrend>>();
		trendPaticipators = new MultiValueMap();
		for (User friend : friends) {
			List<UserTrend> trends = getTrends(friend.getId());
			friendAndHisTrends.put(friend, trends);
			for (UserTrend userTrend : trends) {
				trendPaticipators.put(userTrend.getHotword(), friend);
			}
		}
		for (UserTrend userTrend : trends) {
			trendPaticipators.put(userTrend.getHotword(), user);
		}
	}

	public MultiValueMap getTrendPaticipator() {
		return trendPaticipators;
	}

	public List<User> getTrendPaticipator(String trend) {
		@SuppressWarnings("unchecked")
		List<User> paticipators = (List<User>) trendPaticipators.get(trend);
		return paticipators;
	}

	// ---<actions>-------------------
	public void UpdateStatus(String statuses) throws WeiboException {
		timeline.UpdateStatus(statuses);
	}

	public void trendsFollow(String newTrend) throws WeiboException {
		if (isTrendFollowed(newTrend)) {
			throw new WeiboException("此话题已关注。");
		}
		UserTrend userTrend = trend.trendsFollow(newTrend);
		userTrend.setHotword(newTrend);
		userTrend.setNum("0");
		trends.add(0, userTrend);
		trendPaticipators.put(newTrend, user);
	}

	public void trendsDestroy(int trendId) throws WeiboException {
		trend.trendsDestroy(trendId);
		for (int i = 0; i < trends.size(); ++i) {
			UserTrend one = trends.get(i);
			if (StringUtils.equals(one.gettrendId(), "" + trendId)) {
				trends.remove(i);
				trendPaticipators.remove(one.getHotword(), user);
				break;
			}
		}
	}

	public User getUser() {
		return user;
	}

	public boolean isLogin() {
		return isLogin;
	}

	public String shorturl(final String url) throws WeiboException {
		return shorturl.shorturl(url);
	}

	public boolean isTrendFollowed(String trend) {
		for (UserTrend one : trends) {
			if (StringUtils.equalsIgnoreCase(one.getHotword(), trend)) {
				return true;
			}
		}

		return false;
	}

}
